package org.geek.week1.jvm;

import java.io.BufferedInputStream;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.StringJoiner;

/**
 * @author wangtaoqi
 * @create 2021/9/16
 */
public class HelloClassLoader extends ClassLoader {
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        if (Objects.isNull(name)) {
            return null;
        }

        final byte[] fileBytes = readResource(name);
        final byte[] originalBytes = restore(fileBytes);
        final Class<?> target = super.defineClass(name, originalBytes, 0, originalBytes.length);
        printMethodInfo(target);
        return target;
    }

    private static byte[] restore(byte[] bytes) {
        byte originalByte[] = new byte[bytes.length];
        for (int i = 0; i < bytes.length; i++) {
            final byte b = bytes[i];
            originalByte[i] = (byte) (255 - b);
        }
        return originalByte;
    }

    private byte[] readResource(String name) {
        String findName = name.replace(".", "/");
        byte[] byteArray = new byte[0];
        try (InputStream resourceAsStream = getClass().getResourceAsStream("/" + findName + ".xlass");
             BufferedInputStream bufferedInputStream = new BufferedInputStream(resourceAsStream)) {
            byteArray = new byte[bufferedInputStream.available()];
            bufferedInputStream.read(byteArray, 0, bufferedInputStream.available());
        } catch (Throwable exception) {
            exception.printStackTrace();
        }
        return byteArray;
    }

    private static void printMethodInfo(Class c) {
        if (c == null) {
            return;
        }
        final Method[] declaredMethods = c.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.printf("方法名:%s 参数个数:%s", declaredMethod.getName(), declaredMethod.getParameterCount());
            StringJoiner paramJoin = new StringJoiner(",");
            final Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
            if (parameterTypes != null) {
                for (Class<?> parameterType : parameterTypes) {
                    paramJoin.add(parameterType.getName());
                }
            }
            System.out.printf("参数:%s\n", paramJoin);
        }
    }

    public static void main(String[] args) throws Exception {
        HelloClassLoader helloClassLoader = new HelloClassLoader();
        final String findClassName = "Hello";
        final String findMethodName = "hello-file";

        final Class<?> helloClass = helloClassLoader.findClass(findClassName);
        final Object hello = helloClass.newInstance();
        final Method methodHello = hello.getClass().getMethod(findMethodName, null);
        methodHello.invoke(hello, null);

        System.out.println("操作完成");
    }
}
